package com.nervenets.template.controller.order;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.nervenets.general.config.ApplicationProperties;
import com.nervenets.general.entity.ResponseResult;
import com.nervenets.general.entity.WsMessage;
import com.nervenets.general.exception.LogicException;
import com.nervenets.general.jwt.aspect.JwtRole;
import com.nervenets.general.jwt.aspect.JwtSecurity;
import com.nervenets.general.jwt.util.JwtUtils;
import com.nervenets.general.redis.RedisSyncLocker;
import com.nervenets.general.service.WebSocketService;
import com.nervenets.general.utils.HttpTools;
import com.nervenets.general.utils.JodaUtils;
import com.nervenets.general.utils.SpringContextHolder;
import com.nervenets.general.web.BaseEntityController;
import com.nervenets.general.web.params.IdsParams;
import com.nervenets.template.controller.order.params.RechargeOrderFormParams;
import com.nervenets.template.controller.order.params.RechargeOrderPagingParams;
import com.nervenets.template.enumeration.FlowStatus;
import com.nervenets.template.enumeration.OperateType;
import com.nervenets.template.enumeration.OrderStatus;
import com.nervenets.template.enumeration.UnionType;
import com.nervenets.template.event.DataOfUserEvent;
import com.nervenets.template.hibernate.domain.order.RechargeOrder;
import com.nervenets.template.hibernate.domain.user.User;
import com.nervenets.template.hibernate.domain.user.UserWxId;
import com.nervenets.template.service.RechargeOrderService;
import com.nervenets.template.service.UserService;
import com.nervenets.template.service.WealthFlowService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

@Slf4j
@Api(value = "充值订单管理接口v1", tags = "充值订单管理相关接口")
@RestController
@RequestMapping("/api/recharge/order")
@JwtRole(group = "orders", groupName = "订单管理", function = "recharge", functionName = "充值订单管理", queue = 203)
public class RechargeOrderController extends BaseEntityController<RechargeOrder, RechargeOrderService, RechargeOrderFormParams, RechargeOrderPagingParams, RechargeOrder, RechargeOrder> {
    @Resource
    private UserService userService;
    @Resource
    private WxPayService wxPayService;
    @Resource
    private ApplicationProperties applicationProperties;
    @Resource
    private RedisSyncLocker<ResponseEntity<ResponseResult<RechargeOrder>>> redisSyncLocker;
    @Resource
    private WealthFlowService wealthFlowService;
    @Reference(version = "1.0.0", check = false)
    private WebSocketService webSocketService;

    @Override
    public ResponseEntity<ResponseResult<Void>> del(@Valid @RequestBody IdsParams params, HttpServletRequest request, HttpServletResponse response) {
        throw new LogicException("充值订单不支持删除");
    }

    @Override
    protected RechargeOrder generateListResult(RechargeOrder rechargeOrder, RechargeOrderPagingParams params, HttpServletRequest request, HttpServletResponse response) {
        switch (rechargeOrder.getCreateUserType()) {
            case manager:
                break;
            case teacher:
                break;
            case user:
                User user = userService.findByCache(rechargeOrder.getCreateBy());
                if (null != user) {
                    rechargeOrder.setCreateUserName(user.getName());
                    rechargeOrder.setCreateUserAvatar(user.getAvatar());
                }
                break;
        }
        return rechargeOrder;
    }

    @SneakyThrows
    @Override
    public ResponseEntity<ResponseResult<RechargeOrder>> edit(@Valid @RequestBody RechargeOrderFormParams params, HttpServletRequest request, HttpServletResponse response) {
        ResponseEntity<ResponseResult<RechargeOrder>> responseEntity = super.edit(params, request, response);
        ResponseResult<RechargeOrder> result = responseEntity.getBody();
        assert result != null;
        RechargeOrder order = result.getData();
        Object payInfo = null;
        switch (order.getPayType()) {
            case other:
            case cash:
            case surplus:
            case wxpay:
            case alipay:
            case unionpay:
            case pos:
            case groupBuy:
                return errorMessage("不支持的支付方式");
            case wxpay_online:
                final String notifyUrl = String.format("http://%s/api/recharge/order/wx/callback", applicationProperties.getBaseDomain());
                switch (order.getPlatform()) {
                    case none:
                        break;
                    case h5:
                        UserWxId userWxId = userService.findUserWxId(userService.findOne(JwtUtils.getUser().getId()), wxPayService.getConfig().getAppId());
                        if (null == userWxId) return errorMessage("该账号未绑定微信");
                        payInfo = wxJsapiPay(order.getDetail(), order.getTradeNo(), order.getMoney(), userWxId.getOpenId(), notifyUrl, request);
                        break;
                    case android:
                    case ios:
                        payInfo = wxAppPay(order.getDetail(), order.getTradeNo(), order.getMoney(), notifyUrl, request);
                        break;
                    case management:
                    case desktop:
                    case website:
                        payInfo = wxNativePay(order.getDetail(), order.getTradeNo(), order.getMoney(), String.valueOf(order.getId()), notifyUrl, request);
                        break;
                }
                break;
            case alipay_online:
                break;
        }
        order.setThirdPayInfo(payInfo);
        return successMessage(order);
    }


    @ApiOperation("微信订单回调")
    @PostMapping("/wx/callback")
    @JwtSecurity(required = false)
    public String orderWxCallback(final HttpServletRequest request, HttpServletResponse response) {
        try {
            String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
            WxPayOrderNotifyResult result = wxPayService.parseOrderNotifyResult(xmlResult);
            log.info("微信回调结果: {}", result);
            // 结果正确
            String orderId = result.getOutTradeNo();
            String tradeNo = result.getTransactionId();
            final Integer totalFee = result.getTotalFee();
            final String openid = result.getOpenid();
            if ("SUCCESS".equals(result.getReturnCode()) && "SUCCESS".equals(result.getResultCode())) {
                long id = Long.parseLong(orderId.substring(RechargeOrder.TRADE_NO_PREFIX.length() + String.valueOf(JodaUtils.getTimestamp()).length()));
                //自己处理订单的业务逻辑，需要判断订单是否已经支付过，否则可能会重复调用
                if (orderId.startsWith(RechargeOrder.TRADE_NO_PREFIX)) {
                    String rechargeOrderKey = RechargeOrder.TRADE_NO_PREFIX + id;
                    redisSyncLocker.keyLock(rechargeOrderKey, () -> {
                        service.findById(id).ifPresent(order -> {
                            if (OrderStatus.init.equals(order.getOrderStatus())) {
                                wealthFlowService.wealthChange(
                                        order.getCreateBy(),
                                        order.getActually(),
                                        order.getWealthType(),
                                        OperateType.recharge,
                                        order.getDetail(),
                                        order.getPayType(),
                                        0L, id, UnionType.rechargeOrder, FlowStatus.success
                                );
                                order.setOutTradeNo(tradeNo);
                                order.setOrderStatus(OrderStatus.finish);
                                service.save(order);

                                webSocketService.convertAndSend(rechargeOrderKey, WsMessage.builder().action("paid").data(JSON.toJSONString(order)).build());
                                SpringContextHolder.publishEvent(new DataOfUserEvent(DataOfUserEvent.Type.pay, order.getId()));
                            }
                        });
                        return null;
                    });
                }
            }
            return WxPayNotifyResponse.success("处理成功!");
        } catch (Exception e) {
            log.error("微信回调结果异常,异常原因....", e);
            return WxPayNotifyResponse.fail(e.getMessage());
        }
    }

    protected WxPayNativeOrderResult wxNativePay(String body, String orderNumber, long money, String productId, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .productId(productId)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.NATIVE)
                .build();

        return wxPayService.createOrder(unifiedOrderRequest);
    }

    protected WxPayMpOrderResult wxJsapiPay(String body, String orderNumber, long money, String openId, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.JSAPI)
                .openid(openId)
                .build();
        return wxPayService.createOrder(unifiedOrderRequest);
    }

    protected WxPayAppOrderResult wxAppPay(String body, String orderNumber, long money, String notifyUrl, HttpServletRequest request) throws WxPayException {
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(HttpTools.getIpAddress(request))
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.APP)
                .build();
        return wxPayService.createOrder(unifiedOrderRequest);
    }
}
